package tileStyle;

import java.util.*;
import java.io.*;

/**
 * @author Yuriy Brun
 * 
 * A TileType represents a type of a tile style tile. It has four sides, each
 * with an int label, representing the glue. Each tile also has an int value,
 * and a String name A TileType is immutable.
 * 
 * @param int[]
 *            glues a 4-item int array containing the four glues of a tile
 * @param String
 *            name this tile type's name
 * @param int
 *            value this tile type's value
 * 
 */
public class TileType implements Serializable {
	public static final long serialVersionUID = 1L;
	public static int BLANK_GLUE = -1;
	public static TileType BLANK = new TileType("BLANK", BLANK_GLUE,
			BLANK_GLUE, BLANK_GLUE, BLANK_GLUE, -1);

	public static int NORTH = 0;
	public static int EAST = 1;
	public static int SOUTH = 2;
	public static int WEST = 3;

	protected String name;
	protected int[] glues;
	protected int value;

	/*
	 * Creates a new TileType with the four specified glues, name, and value 
	 * @param String name  the name of this tile type
	 * @param int north    the north glue
	 * @param int east     the east glue
	 * @param int south    the south glue
	 * @param int west     the west glue
	 * @param int value    the value of this tile type
	 */
	public TileType(String name, int north, int east, int south, int west,
			int value) {
		this.name = name;
		glues = new int[4];
		glues[NORTH] = north;
		glues[EAST] = east;
		glues[SOUTH] = south;
		glues[WEST] = west;
		this.value = value;
	}

	/*
	 * Creates a new TileType with the four specified glues
	 * @param String name  the name of this tile type 
	 * @param int[] glues: the four glues
	 * @param int value    the value of this tile type 
	 */
	public TileType(String name, int[] glues, int value) {
		this(name, glues[0], glues[1], glues[2], glues[3], value);
	}
	
	public TileType(TileType original) {
		this(original.name, original.glues[0], original.glues[1], original.glues[2], original.glues[3], original.value);
	}
	
	
	/*
	 * @returns the north glue
	 */
	public int getNorth() {
		return glues[NORTH];
	}

	/*
	 * @returns the east glue
	 */
	public int getEAST() {
		return glues[EAST];
	}

	/*
	 * @returns the south glue
	 */
	public int getSouth() {
		return glues[SOUTH];
	}

	/*
	 * @returns the west glue
	 */
	public int getWest() {
		return glues[WEST];
	}

	/*
	 * @param int direction : specifies the side of the tile type 
	 * @returns the glue specified by direction
	 */
	public int getGlue(int direction) {
		return glues[direction];
	}

	/*
	 * @returns this tile type's name
	 */
	public String getName() {
		return name;
	}

	/*
	 * @returns this tile type's value
	 */
	public int getValue() {
		return value;
	}

	/*
	 * @returns true iff this tile has all blank glues
	 */
	public boolean isBlank() {
		return equals(BLANK);
	}

	/*
	 * @param Iterator<TileType> types : an iterator of tile types 
	 * @param int eastGlue, int southGlue : a desired east and south glue @returns a tile
	 * in types that has the desired eastGlue and southGlue in the east and
	 * south locations, respectively. If there is more than one valid tile, then
	 * one is selected at random.
	 */
	public static TileType getNorthWestMatch(Iterator<TileType> types,
			int eastGlue, int southGlue) {
		List<TileType> possibles = new ArrayList<TileType>();

		while (types.hasNext()) {
			TileType current = types.next();
			if ((current.glues[EAST] == eastGlue)
					&& (current.glues[SOUTH] == southGlue))
				possibles.add(current);
		}
		int choice = (int) (Math.random() * possibles.size());
		if (possibles.size() == 0)
			return null;
		else
			return possibles.get(choice);
	}

	/*
	 * @param obj   the object being compared
	 * @returns true iff obj is equivalent to this tile type
	 */
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final TileType other = (TileType) obj;
		if (!Arrays.equals(glues, other.glues))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (value != other.value)
			return false;
		return true;
	}

	/*
	 * @returns this tile type's hash code
	 */
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + Arrays.hashCode(glues);
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + value;
		return result;
	}

}
